package com.example.networktest;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.content.Context;
import java.util.Enumeration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiConfiguration;
import android.net.DhcpInfo;
import android.text.format.Formatter;
import java.net.NetworkInterface;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.Socket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


/*
 * This class will handle all network communications.  Basically this class 
 * will keep files synced between phones, and will be controlled by android
 * apps that use it to communicate.  This class will rely on another library
 * for authentication.
 */
public class networkingTestService
{
  // Debugging
  private static final String TAG = "networkingTestService";
  private static final boolean D = true;

  // Name for the SDP record when creating server socket
  private static final String NAME = "networkingTestService";

  // Member fields
  private final BluetoothAdapter mAdapter;
  private final Handler mHandler;
  public int mBTState;
  private int mState;
  private String localIPAddress;
  private WifiManager wifiManager;

  // Constants that indicate the current connection state
  public static final int STATE_NONE = 0;       // we're doing nothing
  public static final int STATE_BTLISTEN = 1;     // now listening for incoming connections on bluetooth
  public static final int STATE_BTCONNECTING = 2; // now initiating an outgoing connection on bluetooth
  public static final int STATE_BTCONNECTED = 3;  // now connected to a remote device on bluetooth

  // Server Communication Thread
  private serverComThread servThread;
  


  public static final String LOG_LOCALIP = "Exception Occured While trying to determine local ip.";
  public static final String LOG_DEBUG_MSG = "THIS IS A DEBUGGING MESSAGE --------------->";

  /**
   * Constructor. Creates a new networkTestService session.
   * @param context  The UI Activity Context
   * @param handler  A Handler to send messages back to the UI Activity
   */
  public networkingTestService(Context context, Handler handler) 
  {
    Log.d(LOG_DEBUG_MSG, "Starting up Network Service");
    wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mBTState = STATE_NONE;
    mHandler = handler;
    Log.d(LOG_DEBUG_MSG, "Starting up Server Thread");
    servThread = new serverComThread("12");
    servThread.start();
  }

  public String getLocalIPAddress() 
  {
    return localIPAddress;
  }

  /**
   * Determine IP address of the local phone, if it returns NULL, then there is no connection available.
   * This method returns 0 if there is an available connection and 1 otherwise.
   */
  public synchronized int refreshLocalIPAddress() 
  {
    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
    DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
    int ipAddress = wifiInfo.getIpAddress();
    localIPAddress = Formatter.formatIpAddress(dhcpInfo.ipAddress)+"and"+Formatter.formatIpAddress(ipAddress);

    return 0;

    // I found this online, but it dosen't work...
    //try 
    //{
    //  for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) 
    //  {
    //    NetworkInterface intf = en.nextElement();
    //    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) 
    //    {
    //      InetAddress inetAddress = enumIpAddr.nextElement();
    //      if (!inetAddress.isLoopbackAddress()) 
    //      {
    //        localIPAddress = inetAddress.getHostAddress().toString();
    //        return 0;
    //      }
    //    }
    //  }
    //} 
    //catch (SocketException ex) 
    //{
    //  Log.e(LOG_LOCALIP, ex.toString());
    //}
    //localIPAddress = null;
    
    //try{
    //Socket theSocket = new Socket("127.0.0.1",8888);
    //ServerSocket theSocket = new ServerSocket(8898);
    //localIPAddress = theSocket.getInetAddress().getHostAddress();
    //theSocket.close();
    //}
    //catch (IOException ex) 
    //{
    //  localIPAddress = ex.toString();
    //  Log.e(LOG_LOCALIP, ex.toString());
    //}
  }

  // This thread handles commmunication with the server
  private class serverComThread extends Thread 
  {
    // Socket used to communicate with the server

    private String address;
    private Socket mainSocket;
    private int port;
    private int connected;
    private SocketAddress inet;
    private InputStream servIn;
    private OutputStream servOut;
    private String mID;

    public serverComThread(String ID)
    {
      mainSocket=null;
      address = "192.168.1.106";
      port = 7778;
      servIn=null;
      servOut=null;
      mID = ID;
      connected=0;
      //inet = new SocketAddress(address, port);
    }

    public void run()
    {
      // Connect to the server
      int output = connectToServer();
      Log.d(LOG_DEBUG_MSG, "Connection status is "+Integer.toString(output));
      if(output !=0)
      {
        Log.d(LOG_DEBUG_MSG, "Canceling");
        cancel();
      }
      
      // Fetch ip addresses
      if(getPeerIP("12") != 0)
        cancel();

    }

    // WHERE IS THE UNCAUGHT EXCEPTION?
    public void cancel()
    {
      Log.d(LOG_DEBUG_MSG, "Server Thread Shutting Down");
      Log.d(LOG_DEBUG_MSG, "Socket status is "+Boolean.toString(mainSocket!=null));
      Log.d(LOG_DEBUG_MSG, "Connection status is "+Integer.toString(connected));

      if(mainSocket!=null && connected==1)
      {
        if(!mainSocket.isConnected())
        {
          try
          {
            mainSocket.close();
          }
          catch (IOException e) 
          {
            Log.e(TAG, "close() of socket failed", e);
          }
        }
      }
      mainSocket=null;
      Log.d(LOG_DEBUG_MSG, "Exiting");
    }

    // Recieves a message from the server that is no longer than
    // maxLength bytes
    public String recvMessageFromServer(int maxLength)
    {
      try
      {
        // If we are not connected to the server, then return an empty string
        if(mainSocket==null || !mainSocket.isConnected() || servIn==null)
          return "";

        // Read the message, and record its length
        int byteNum=0;
        byte[] msg = new byte[maxLength];
        byteNum = servIn.read(msg,0,maxLength);
        if(byteNum==maxLength)
        {
          Log.d(LOG_DEBUG_MSG, "the message received from the server was truncated.");
          return new String(msg);
        }
        Log.d(LOG_DEBUG_MSG, "Recieved message length is "+Integer.toString(byteNum));
        // Then put the message in a smaller array and convert that array to 
        // a string
        if(byteNum==0)
          return "";
        byte[] trimmedMsg = new byte[byteNum];
        System.arraycopy(msg, 0, trimmedMsg, 0, byteNum);
        return new String(trimmedMsg);
      }
      catch(IOException ex)
      {
        Log.d(LOG_DEBUG_MSG, "Unable to recieve message from server");
        Log.e(LOG_LOCALIP, ex.toString());
        return "";
      }
    }

    // Get the IP address of the given peer from the server
    public int getPeerIP(String peerID)
    {
      try
      {
        // Send the server the ID we want info about
        servOut.write(peerID.getBytes());
        // Recieve the server's response
        String Message = recvMessageFromServer(256);
        Log.d(LOG_DEBUG_MSG, "Recieved message "+Message);
        // If this peer is online
        if(Message.compareTo("i")==0)
        {
          // Recieve the IP and Port
          String IP = recvMessageFromServer(256);
          Log.d(LOG_DEBUG_MSG, "The IP is "+IP);
          //String Port = recvMessageFromServer(256);
          //Log.d(LOG_DEBUG_MSG, "The port is "+Port);
          return 0;
        }
        else
          return 2;
      }
      catch(IOException ex)
      {
        Log.d(LOG_DEBUG_MSG, "Unable to get Peer IP");
        Log.e(LOG_LOCALIP, ex.toString());
        return 1;
      }
    }

    public int connectToServer()
    {
      try
      {
        Log.d(LOG_DEBUG_MSG, "Trying to connect to "+address);
        // Open the socket if needed
        if(mainSocket==null) mainSocket = new Socket(address,port);

        // TODO: Reconnect to the server if needed
        //if(!mainSocket.isConnected()) 
        //mainSocket.connect(inet);
        
        servIn = mainSocket.getInputStream();
        servOut = mainSocket.getOutputStream();

        // Send the server our id
        servOut.write(mID.getBytes());
        // Recieve an acknowledgement from the server
        String Message = recvMessageFromServer(10);
        Log.d(LOG_DEBUG_MSG, "Recieved message "+Message);
        if(Message.compareTo("r")==0)
        {
          connected=1;
          return 0;
        }
        else
          return 1;
      }
      catch(IOException ex)
      {
        Log.d(LOG_DEBUG_MSG, "Unable to connect to server");
        Log.e(LOG_LOCALIP, ex.toString());
        return 1;
      }
    }
  }
}

